package cn.com.spbun.nddd.service;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.apache.log4j.Logger;

import cn.com.spbun.common.FileUtil;
import cn.com.spbun.common.Inflector;
import cn.com.spbun.common.TmodelUtil;
import cn.com.spbun.nddd.gui.console.ConsoleDialog;
import cn.com.spbun.nddd.model.ColumnInfo;
import cn.com.spbun.nddd.model.ConnectionInfo;
import cn.com.spbun.nddd.model.ForeignKeyInfo;
import cn.com.spbun.nddd.model.TableInfo;
import cn.com.spbun.nddd.model.TmodelClassDetail;
import cn.com.spbun.nddd.model.TmodelFieldDetail;
import cn.com.spbun.nddd.service.database.mysql.AbstractMysqlAnaylizerService;
import freemarker.cache.FileTemplateLoader;
import freemarker.cache.TemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;

/**
 * 生成Tmodel
 * 
 * @author NOLY DAKE
 *
 */
public class TmodelMakeService {

    /** 日志对象 */
    private static final Logger LOG = Logger.getLogger(TmodelMakeService.class);

    /** 输出目录 */
    private File outputForder = null;
    /** 数据库表信息 */
    private List<TableInfo> tableInfos = null;
    /** 表的前缀 */
    private List<String> tablePrefix = null;
    /** 包名 */
    private String packageName = null;

    /**
     * 命令输出
     */
    private ConsoleDialog console = null;

    public void setConsole(ConsoleDialog console) {
        this.console = console;
    }

    public TmodelMakeService(ConnectionInfo connectionInfo, File file, List<String> tablePrefix,
            List<TableInfo> tableInfos, String packageName) {
        this.outputForder = file;
        this.tablePrefix = tablePrefix;
        this.tableInfos = tableInfos;
        this.packageName = packageName;

    }

    /**
     * 将信息导出到文件中
     */
    public void makeTmodel() {

        if (console != null) {
            console.console("在输出目录创建model目录开始...");
        }

        File tempForder = new File(outputForder, "model");
        tempForder.mkdirs();

        if (console != null) {
            console.console("开始生成Tmodel...");
        }

        Template template = loadTableTemplete();

        // 异常校验失败，不生成java类的表 by wuxh
        Iterator<TableInfo> it = tableInfos.iterator();
        List<TableInfo> middleTables = new ArrayList<TableInfo>();
        while (it.hasNext()) {
            TableInfo tableInfo = it.next();

            boolean pass = validateTable(tableInfo);

            if (pass) {
                continue;
            }

            middleTables.add(tableInfo);
            it.remove();
        }

        for (int i = 0; i < tableInfos.size(); i++) {

            TableInfo tableInfo = tableInfos.get(i);
            if (tableInfo.getTableName().equals("zyy_user_role")) {
                System.out.println("--------");
            }

            FileOutputStream fos = null;
            BufferedOutputStream bos = null;
            OutputStreamWriter osw = null;

            try {

                TmodelClassDetail detail = conventTableInfo2TmodelInfo(tableInfo, tableInfos, middleTables,
                        packageName);

                fos = new FileOutputStream(new File(tempForder, detail.getClassName() + ".java"), false);

                bos = new BufferedOutputStream(fos);
                osw = new OutputStreamWriter(bos, "UTF-8");

                if (console != null) {
                    console.console("[" + (i + 1) + "/" + tableInfos.size() + "]生成Tmodel：" + detail.getClassName());
                }
                template.process(detail, osw);
            } catch (TemplateException e) {
                LOG.error(e.getMessage(), e);
                if (console != null) {
                    console.console("处理过程出现异常，异常信息：" + e.getMessage());
                }
            } catch (IOException e) {
                LOG.error(e.getMessage(), e);
                if (console != null) {
                    console.console("处理过程出现异常，异常信息：" + e.getMessage());
                }
            } finally {
                if (osw != null) {
                    try {
                        osw.close();
                    } catch (IOException e) {
                        LOG.error(e.getMessage(), e);
                    }
                }
                if (bos != null) {
                    try {
                        bos.close();
                    } catch (IOException e) {
                        LOG.error(e.getMessage(), e);
                    }
                }
                if (fos != null) {
                    try {
                        fos.close();
                    } catch (IOException e) {
                        LOG.error(e.getMessage(), e);
                    }
                }
            }
        }
    }

    /**
     * 校验表，校验不通过的表不能持久化到文件
     * 
     * @param tableInfo
     * @return
     */
    private boolean validateTable(TableInfo tableInfo) {
        if (tableInfo == null) {
            return false;
        }

        // 两个字段均是主键则认为是中间表
        // 校验表是否是中间表，中间表不生成
        List<ColumnInfo> columnInfos = tableInfo.getColumnInfos();
        if (columnInfos != null && columnInfos.size() == 2) {

            for (ColumnInfo columnInfo : columnInfos) {

                if (!columnInfo.getIsPrimaryKey()) {
                    return true;
                }
            }
            return false;

        }

        return true;
    }

    /**
     * 将一条数据库表信息转换为Tmodel模型系想你
     * 
     * @param tableInfo
     *            数据库表信息
     * @param tableInfos
     *            数据库中的所有表信息
     * @param packageName
     *            要输出的包名
     * @return
     */
    private TmodelClassDetail conventTableInfo2TmodelInfo(TableInfo tableInfo, List<TableInfo> tableInfos,
            List<TableInfo> middleTables, String packageName) {

        TmodelClassDetail detail = new TmodelClassDetail();

        detail.setPackageName(packageName);// 包名称
        detail.setTableName(tableInfo.getTableName()); 
        detail.setTableDescribe(tableInfo.getTableDescribe().replaceAll("\r\n", ""));
 
        // 获得不包含前缀的数据库表名称

        String tableName = TmodelUtil.removePrefix(tableInfo.getTableName(), tablePrefix);// 移除表中的前缀信息

        String className = TmodelUtil.convent2ClassName(tableName);// 类的名称
        detail.setClassName(className);

        String instanceName = TmodelUtil.convent2InstanceName(tableName);// 实例的名称
        detail.setInstanceName(instanceName);

        List<TmodelFieldDetail> details = new ArrayList<TmodelFieldDetail>();
        detail.setFieldDetails(details);
        {

            // 外键信息
            List<ForeignKeyInfo> foreignKeyInfoList = tableInfo.getForeignKeyInfos();
            List<ColumnInfo> columnInfoList = tableInfo.getColumnInfos();// 字段信息

            for (ColumnInfo columnInfo : columnInfoList) {

                TmodelFieldDetail fdetail = new TmodelFieldDetail();
                details.add(fdetail);

                fdetail.setCanNull(columnInfo.getIsCanNull());
                fdetail.setColumnLength(columnInfo.getColumnLength());
                fdetail.setDescription(columnInfo.getDescription());

                // 字段数据库名称
                String columnName = columnInfo.getColumnNameEn();

                ForeignKeyInfo fkInfo = findFKInfo(columnName, foreignKeyInfoList);

                if (fkInfo != null) {

                    detail.setHasFK(true);
                    String refTableName = TmodelUtil.removePrefix(fkInfo.getReferenceTableName(), tablePrefix);

                    fdetail.setColumnEnName(columnName);

                    String instance = TmodelUtil.convent2InstanceName(refTableName);
                    String objectType = TmodelUtil.convent2ClassName(refTableName);
                    boolean isEixct = false;

                    if (details.size() > 0) {
                        for (TmodelFieldDetail det : details) {
                            String instance_ = det.getFieldInstanceType();
                            String objectType_ = det.getFieldObjectType();

                            if (!objectType.equals(objectType_)) {
                                continue;
                            }

                            if (instance.equals(instance_)) {

                                det.setFieldInstanceType(
                                        instance_ + "By" + TmodelUtil.column2ClazzForUpper(det.getColumnEnName()));
                            }

                            isEixct = true;

                        }
                    }
                    if (isEixct) {
                        instance = instance + "By" + TmodelUtil.column2ClazzForUpper(columnName);
                    }
                    fdetail.setFieldInstanceType(instance);
                    fdetail.setFieldObjectType(TmodelUtil.convent2ClassName(refTableName));
                    fdetail.setFK(true);

                } else {

                    // 字段数据库类型
                    String col_type = columnInfo.getColumnType();

                    // 字段model类型
                    String java_type = AbstractMysqlAnaylizerService.COLUMNTYPE_JAVATYPE.get(col_type);

                    if (java_type.equals("Date")) {
                        detail.setHasDateField(true);
                        fdetail.setHasDateField(true);
                    }
                    if (java_type.equals("BigDecimal")) {
                        detail.setHasDecimalField(true);
                    }
                    if (java_type.equals("Integer")) {
                        if (!fdetail.getCanNull()) {
                            java_type = "int";
                        }
                    }

                    fdetail.setPK(columnInfo.getIsPrimaryKey());
                    fdetail.setColumnEnName(columnName);
                    fdetail.setFieldObjectType(java_type);
                    fdetail.setFieldInstanceType(TmodelUtil.column2ClazzForLower(columnName));
                }
            }
        }

        // 设置被引用信息
        {

            for (TableInfo subTableInfo : tableInfos) {

                List<ForeignKeyInfo> foreignKeyInfoList = subTableInfo.getForeignKeyInfos();// 其他表的外键

                if (foreignKeyInfoList == null || foreignKeyInfoList.size() == 0) {
                    continue;
                }

                for (ForeignKeyInfo fkinfo : foreignKeyInfoList) {

                    String refTableNname = fkinfo.getReferenceTableName();
                    if (!refTableNname.equals(tableInfo.getTableName())) {
                        continue;
                    }

                    detail.setHasRK(true);

                    TmodelFieldDetail fdetail = new TmodelFieldDetail();
                    details.add(fdetail);

                    String keyTabName = TmodelUtil.removePrefix(fkinfo.getTableName(), tablePrefix);

                    fdetail.setRK(true);
                    fdetail.setColumnEnName(fkinfo.getColumnName());
                    
                    String instance = TmodelUtil.convent2InstanceName(keyTabName);
                    String objectType = TmodelUtil.convent2ClassName(keyTabName);
                    instance =Inflector.getInstance().pluralize(instance);
                    String instanceName_ = instanceName;
                    boolean isEixct = false;

                    for (TmodelFieldDetail det : details) {

                        if (!det.getIsRK()) {
                            if (instance.equals(det.getFieldInstanceType())) {
                            	
                                isEixct=true;
                                break;
                            }
                            continue;
                        }
                        String instance_ = det.getFieldInstanceType();
                        String objectType_ = det.getFieldObjectType();

                        if (!("Set<" + objectType + ">").equals(objectType_)) {
                            continue;
                        }

                        if (instance.equals(instance_)) {

                            det.setFieldInstanceType(
                                    instance_ + "By" + TmodelUtil.column2ClazzForUpper(det.getColumnEnName()));
                            det.setRkFieldInstanceType(
                                    instanceName_ + "By" + TmodelUtil.column2ClazzForUpper(det.getColumnEnName()));
                        }

                        isEixct = true;

                    }

                    if (isEixct) {
                        instance = instance + "By" + TmodelUtil.column2ClazzForUpper(fkinfo.getColumnName());
                        instanceName_ = instanceName_ + "By" + TmodelUtil.column2ClazzForUpper(fkinfo.getColumnName());
                    }
                    
                    
                    List<ColumnInfo> columnInfoList = tableInfo.getColumnInfos();// 字段信息
                    String description="";
                    for (ColumnInfo columnInfo : columnInfoList) {
                       
                        // 字段数据库名称
                        String columnName = columnInfo.getColumnNameEn();
                        if(columnName.equals(fdetail.getColumnEnName())){
                        	 description=columnInfo.getDescription();
                        	 break;
                        }
                       
                    }
                    fdetail.setDescription(description);
                    fdetail.setRkFieldInstanceType(instanceName_);
                    fdetail.setFieldInstanceType(instance);
                    fdetail.setFieldObjectType("Set<" + TmodelUtil.convent2ClassName(keyTabName) + ">");

                }
            }
        }

        {

            // 中间表的处理，2016-4-7 by wuxh
            List<TableInfo> tmpMiddleTables = findMiddleTable(middleTables, tableInfo);
            for (TableInfo middleTable : tmpMiddleTables) {

                List<ForeignKeyInfo> foreignKeyInfoList = middleTable.getForeignKeyInfos();// 其他表的外键

                if (foreignKeyInfoList == null || foreignKeyInfoList.size() != 2) {
                    continue;
                }

                for (int i = 0; i < foreignKeyInfoList.size(); i++) {
                    ForeignKeyInfo fkinfo = foreignKeyInfoList.get(i);

                    String refTableNname = fkinfo.getReferenceTableName();
                    if (refTableNname.equals(tableInfo.getTableName())) {
                        continue;
                    }

//                    detail.setHasRK(true);

                    TmodelFieldDetail fdetail = new TmodelFieldDetail();
                    details.add(fdetail);

                    String keyTabName = TmodelUtil.removePrefix(refTableNname, tablePrefix);

                    // 获取另一个外键的列名
                    fdetail.setColumnEnName(foreignKeyInfoList.get(i == 0 ? 1 : 0).getColumnName());
                    fdetail.setMDK(true);
                    detail.setMDK(true);
                    fdetail.setForeignKeyColumnName(fkinfo.getColumnName());
                    fdetail.setForeignKeyTableName(fkinfo.getTableName());

                    String instance = TmodelUtil.convent2InstanceName(keyTabName);
                    fdetail.setFieldInstanceType(Inflector.getInstance().pluralize(instance));
                    fdetail.setFieldObjectType("Set<" + TmodelUtil.convent2ClassName(keyTabName) + ">");

                }
            }

        }

        return detail;

    }

    /**
     * 查找一个表的中间关联表
     * 
     * @param middleTables
     * @param tableInfo
     * @return
     */
    private List<TableInfo> findMiddleTable(List<TableInfo> middleTables, TableInfo tableInfo) {
        List<TableInfo> tmpMiddleTables = new ArrayList<TableInfo>();
        for (TableInfo info : middleTables) {
            List<ForeignKeyInfo> foreignKeyInfos = info.getForeignKeyInfos();
            if (foreignKeyInfos == null) {
                continue;
            }

            boolean isMiddleTable = false;
            for (ForeignKeyInfo foreignKeyInfo : foreignKeyInfos) {
                if (foreignKeyInfo.getReferenceTableName().equals(tableInfo.getTableName())) {
                    isMiddleTable = true;
                    break;
                }
            }
            if (isMiddleTable) {
                tmpMiddleTables.add(info);
            }
        }
        return tmpMiddleTables;
    }

    private ForeignKeyInfo findFKInfo(String columnName, List<ForeignKeyInfo> foreignKeyInfoList) {

        for (ForeignKeyInfo finfo : foreignKeyInfoList) {
            String f_col_name = finfo.getColumnName();
            if (f_col_name.equals(columnName)) {
                return finfo;
            }
        }

        return null;
    }

    private Template loadTableTemplete() {

        // 创建配置实例
        Configuration configuration = new Configuration(Configuration.VERSION_2_3_23);
        // 设置编码
        configuration.setDefaultEncoding("UTF-8");

        File forder = new File(FileUtil.getInstallPath(), "resources");
        File file = new File(forder, "templete");

        Template template = null;

        try {
            TemplateLoader templateLoader = new FileTemplateLoader(file);

            // ftl模板文件统一放至 com.lun.template 包下面
            configuration.setTemplateLoader(templateLoader);

            // 获取模板
            template = configuration.getTemplate("tmodel.tlp");

        } catch (IOException e) {
            LOG.error(e.getMessage(), e);
        }

        return template;
    }
}
